package com.hksj.Intelligentmanagement.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import lombok.Data;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.xmlbeans.impl.common.IOUtil;

import javax.imageio.ImageIO;
import javax.validation.constraints.NotNull;
import java.awt.Color;
import java.awt.Font;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ExcelToImageUtil {

    /**
     * 刷新内嵌对象内容
     * @param writer
     * @param dataMap
     * @param embedSheetIndex
     * @param embedImageIndex
     * @param targetWidth
     * @param targetHeight
     * @param scale
     * @param propertyKeys
     */
    public static void refreshEmbedObj(ExcelWriter writer,Map<String, String> dataMap,Integer embedSheetIndex,Integer embedImageIndex,Integer targetWidth,Integer targetHeight,Double scale
            ,List<String> propertyKeys,Boolean showBorder,Map<String,DrawingCellInfo> cellStyleMap){
        //排班右上
        XSSFSheet sheet = (XSSFSheet) writer.getSheet();
        PackagePart packagePart = sheet.getRelations().get(embedSheetIndex).getPackagePart();
        ExcelWriter embedWriter = null;
        try {
            embedWriter = ExcelUtil.getReader(packagePart.getInputStream()).getWriter();
            embedWriter.getCellStyle().setWrapText(true);
            if (!showBorder){
                embedWriter.getCellStyle().setTopBorderColor(IndexedColors.WHITE.getIndex());
                embedWriter.getCellStyle().setBottomBorderColor(IndexedColors.WHITE.getIndex());
                embedWriter.getCellStyle().setLeftBorderColor(IndexedColors.WHITE.getIndex());
                embedWriter.getCellStyle().setRightBorderColor(IndexedColors.WHITE.getIndex());
            }
            for (int i = 0; i < embedWriter.getRowCount(); i++) {
                Row row = embedWriter.getOrCreateRow(i);
                for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
                    Cell cell = embedWriter.getCell(j,i);
                    if (ObjectUtil.isNotEmpty(cell)){
                        String propertyName = ExcelToImageUtil.getTemplateKey(cell.getStringCellValue());
                        if (propertyKeys.contains(propertyName)) {
                            embedWriter.writeCellValue(j,i,dataMap.getOrDefault(propertyName,""));
                            if (ObjectUtil.isNotEmpty(cellStyleMap) && cellStyleMap.getOrDefault(j + "" + i,new DrawingCellInfo()).getAlign() == 1){
                                CellStyle currentCellStyle = embedWriter.createCellStyle();
                                currentCellStyle.cloneStyleFrom(embedWriter.getOrCreateCellStyle(j,i));
                                currentCellStyle.setAlignment(HorizontalAlignment.LEFT);
                                cell.setCellStyle(currentCellStyle);
                            }
                        }
                    }
                }
            }
            embedWriter.flush(packagePart.getOutputStream(),true);

            BufferedImage image = ExcelToImageUtil.toImage(embedWriter, targetWidth, targetHeight,showBorder,cellStyleMap);
            image = Thumbnails.of(image).scale(scale).asBufferedImage();
            OutputStream imageOut = sheet.getRelations().get(embedImageIndex).getPackagePart().getOutputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(image, "png", baos);
            byte[] bytes = baos.toByteArray();
            IOUtil.copyCompletely(new ByteArrayInputStream(bytes),imageOut);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            if (ObjectUtil.isNotEmpty(embedWriter)){
                embedWriter.close();
            }
        }
    }

    public static BufferedImage toImage(ExcelWriter writer, Integer targetWidth, Integer targetHeight,Boolean showBorder,Map<String,DrawingCellInfo> cellStyleMap){
        //画图
        Color background = new Color(255, 255, 255);
        Color color = new Color(0, 0, 0);
        Font font = new Font("宋体", Font.PLAIN, 24);
        return toImage(writer,targetWidth,targetHeight,background,color,font,showBorder,cellStyleMap);
    }

    public static BufferedImage toImage(ExcelWriter writer, Integer targetWidth, Integer targetHeight,Color background, Color color,
                                        Font font,Boolean showBorder,Map<String,DrawingCellInfo> cellStyleMap){
        //集合
        Map<String, DrawingCellInfo> cellInfoMap = new HashMap<>();
        //获取工作表
        Sheet sheet = writer.getSheet();
        //实际宽高
        Integer realTotalWidth = 0;
        Integer realTotalHeight = 0;
        //起点坐标
        Integer x = 0;
        Integer y = 0;
        Integer nextX = 0;
        Integer nextY = 0;
        //逐行逐列迭代
        for (int rowNum = 0; rowNum < writer.getRowCount(); rowNum++) {
            //获取行
            Row row = writer.getOrCreateRow(rowNum);
            Integer lineHeight = (int) row.getHeight();
            nextY = y + lineHeight;
            realTotalHeight += lineHeight;

            //获取行中的每一列
            for (int colNum = 0; colNum < writer.getColumnCount(rowNum); colNum++) {
                //获取单元格
                Cell cell = row.getCell(colNum);
                String value = getCellValue(cell);

                Integer width =  sheet.getColumnWidth(cell.getColumnIndex());
                nextX = x + width;

                //统计第一行的宽度
                if (rowNum == 0){
                    realTotalWidth += width;
                }

                //封装单元格坐标信息
                DrawingCellInfo cellInfoDTO = new DrawingCellInfo();
                cellInfoDTO.setKey(colNum + "" + rowNum);
                cellInfoDTO.setValue(value);
                cellInfoDTO.setStartX(x);
                cellInfoDTO.setStartY(y);
                cellInfoDTO.setEndX(nextX);
                cellInfoDTO.setEndY(nextY);
                cellInfoDTO.setWidth(width);
                cellInfoDTO.setHeight(lineHeight);
                if (ObjectUtil.isNotEmpty(cellStyleMap)){
                    DrawingCellInfo cellStyle = cellStyleMap.get(cellInfoDTO.getKey());
                    if (ObjectUtil.isNotEmpty(cellStyle)){
                        cellInfoDTO.setFont(cellStyle.getFont());
                        cellInfoDTO.setAlign(cellStyle.getAlign());
                    }
                }
                cellInfoMap.put(cellInfoDTO.getKey(),cellInfoDTO);

                x = nextX;
            }

            //初始化下一行的坐标
            x = 0;
            y = nextY;
        }

        //合并单元格
        handleMergedRegions(sheet,cellInfoMap);

        //计算实际坐标
        resize(cellInfoMap,realTotalWidth,realTotalHeight,targetWidth,targetHeight);

        List<DrawingCellInfo> drawingCellInfos = cellInfoMap.values().stream().collect(Collectors.toList());
        return createImage(drawingCellInfos, background, color,font, targetWidth, targetHeight,showBorder);
    }

    /**
     * 处理合并单元格
     */
    public static void handleMergedRegions(Sheet sheet,Map<String, DrawingCellInfo> cellInfoMap){
        //获取合并单元格
        List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();

        //处理单元格
        for (CellRangeAddress mergedRegion : mergedRegions) {
            //获取首末坐标
            DrawingCellInfo start = cellInfoMap.get(mergedRegion.getFirstColumn() + "" + mergedRegion.getFirstRow());
            DrawingCellInfo end = cellInfoMap.get(mergedRegion.getLastColumn() + "" + mergedRegion.getLastRow());

            //封装合并单元格坐标信息
            DrawingCellInfo mergedRegionCellInfo = new DrawingCellInfo();
            mergedRegionCellInfo.setValue(start.getValue());
            mergedRegionCellInfo.setStartX(start.getStartX());
            mergedRegionCellInfo.setStartY(start.getStartY());
            mergedRegionCellInfo.setEndX(end.getEndX());
            mergedRegionCellInfo.setEndY(end.getEndY());
            mergedRegionCellInfo.setKey(start.getKey());
            mergedRegionCellInfo.setFont(start.getFont());
            mergedRegionCellInfo.setAlign(start.getAlign());
            mergedRegionCellInfo.setHeight(mergedRegionCellInfo.getEndY() - mergedRegionCellInfo.getStartY());
            mergedRegionCellInfo.setWidth(mergedRegionCellInfo.getEndX() - mergedRegionCellInfo.getStartX());

            //移除被合并的单元格
            for (int i = mergedRegion.getFirstRow(); i <= mergedRegion.getLastRow(); i++) {
                for (int j = mergedRegion.getFirstColumn(); j <= mergedRegion.getLastColumn()  ; j++) {
                    cellInfoMap.remove(j + "" + i);
                }
            }

            //添加合并的单元格
            cellInfoMap.put(mergedRegionCellInfo.getKey(), mergedRegionCellInfo);

        }
    }

    private static void resize(Map<String, DrawingCellInfo> cellInfoMap,Integer realTotalWidth, Integer realTotalHeight,Integer targetWidth,Integer targetHeight){
        //计算实际宽高系数比
        Float widthRatio = targetWidth * 1.0F / realTotalWidth;
        Float heightRatio = targetHeight * 1.0F / realTotalHeight;
        for (DrawingCellInfo item : cellInfoMap.values()) {
            item.setWidth((int) (item.getWidth() * widthRatio));
            item.setHeight((int) (item.getHeight() * heightRatio));
            item.setStartX((int) (item.getStartX() * widthRatio));
            item.setEndX((int) (item.getEndX() * widthRatio));
            item.setStartY((int) (item.getStartY() * heightRatio));
            item.setEndY((int) (item.getEndY() * heightRatio));
        }
    }

    /**
     * 图片生成
     */
    public static BufferedImage createImage(List<DrawingCellInfo> list, Color background, Color color, Font font, Integer targetWidth, Integer targetHeight,Boolean showBorder){
        // 创建image
        BufferedImage image = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_4BYTE_ABGR);

        Graphics2D graphics = image.createGraphics();
        graphics.setBackground(background);
        graphics.clearRect(0, 0, image.getWidth(), image.getHeight());

        for (DrawingCellInfo cellInfo : list) {

            graphics.setColor(color);
            graphics.setFont(font);
            //画格子
            if (showBorder){
                graphics.drawRect(cellInfo.getStartX(),cellInfo.getStartY(),cellInfo.getWidth(),cellInfo.getHeight());
            }
            //设置单元格字体
            if (ObjectUtil.isNotEmpty(cellInfo.getFont())){
                graphics.setFont(cellInfo.getFont());
            }

            FontMetrics metrics = graphics.getFontMetrics(graphics.getFont());
            Integer textWidth = metrics.stringWidth(cellInfo.getValue());
            Integer textHeight = metrics.getHeight();

            Integer x = 0;
            Integer y = 0;

            //多行处理
            List<String> values = splitToMulti(cellInfo.getValue());
            Integer rowNum = values.size() > 0 ? values.size() : 1;
            for (int i = 1; i <= values.size(); i++) {

                //设置单元格水平位置
                if (ObjectUtil.isNotEmpty(cellInfo.getAlign()) && cellInfo.getAlign() == 1){
                    //居左
                    x = cellInfo.getStartX();
                    y = cellInfo.getHeight() / rowNum * (i - 1)
                            + (cellInfo.getHeight() / rowNum - textHeight) / 2 + metrics.getAscent() + cellInfo.getStartY();
                }else{
                    //居中
                    x = (cellInfo.getWidth() - textWidth) / 2 + cellInfo.getStartX();
                    y = cellInfo.getHeight() / rowNum * (i - 1)
                            + (cellInfo.getHeight() / rowNum - textHeight) / 2 + metrics.getAscent() + cellInfo.getStartY();
                }
                //画字
                graphics.drawString(values.get(i - 1), x, y);
            }
        }
        graphics.dispose();
        return image;
    }

    public static List<String> splitToMulti(String value){
        Integer baseWordNum = 20 ;
        int length = value.length();
        Integer size = length / baseWordNum;
        if (length % baseWordNum > 0){
            size += 1;
        }
        List<String> values = new ArrayList<>();
        for (Integer i = 1; i <= size; i++) {
            if (i == size){
                values.add(value.substring((i - 1) * baseWordNum,length));
            }else{
                values.add(value.substring((i - 1) * baseWordNum,i * baseWordNum));
            }
        }
        return values;
    }

    /**
     * 获取单元格的值
     * @param cell 单元格
     * @return
     */
    private static String getCellValue(Cell cell){
        String value = "";
        if (CellType.NUMERIC.equals(cell.getCellType())){
            value = String.valueOf(cell.getNumericCellValue());
        }else if (CellType.STRING.equals(cell.getCellType())){
            value = cell.getStringCellValue();
        }
        return value;
    }

    /**
     * 根据模板占位符获取字段名
     * @param placeholder
     * @return
     */
    public static String getTemplateKey(@NotNull String placeholder){
        return placeholder.replace("{{","").replace("}}","");
    }

    public static DrawingCellInfo createDrawingCellInfo(String key,Integer align,Font font){
        DrawingCellInfo cellInfo = new DrawingCellInfo();
        cellInfo.setKey(key);
        cellInfo.setAlign(align);
        cellInfo.setFont(font);
        return cellInfo;
    }

    @Data
    public static class DrawingCellInfo {
        public static final Integer ALIGN_LEFT = 1;
        public static final Integer ALIGN_CENTER = 2;

        private Integer startX;
        private Integer startY;
        private Integer endX;
        private Integer endY;
        private String key;
        private String value;
        private Integer width;
        private Integer height;
        private Font font;
        private Integer align = 2; //1 = left 2 = center
    }
}
